﻿using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;
using Microsoft.Xna.Framework.Graphics;

namespace Cobalt.Content.Pipeline.Graphics.Illumination
{
	internal class Lightmap
	{
		public const int MaxDimension = 1 << 16;

		public Lightmap( int width, int height, int samples, int numAccumTargets )
		{
			this.width = width;
			this.height = height;
			this.samples = samples;

			this.samplePitch = width * samples;
			this.luxelPitch = samplePitch * samples;

			sampleCount = width * height * samples * samples;

			sampleDescs = new SampleDesc[sampleCount];

			accumTargets = new Vector3[numAccumTargets][];
			for( int i = 0; i < accumTargets.Length; i++ )
				accumTargets[i] = new Vector3[sampleCount];
		}

		private int width;
		private int height;
		private int samples;
		private int sampleCount;

		private int samplePitch;
		private int luxelPitch;

		public struct SampleDesc
		{
			public Vector3 Position;
			public Vector3 Normal;
			public Vector3 Tangent;
			public Vector3 Binormal;

			//public Vector2 LightmapCoord;
			//public Vector2 TextureCoord;

			public IlluminationEngine.Surface Source;
		}

		public struct Vertex
		{
			public Vector2 LightmapCoord;

			public Vector3 Position;
			public Vector3 Normal;
			public Vector3 Tangent;
			public Vector3 Binormal;

			public Vector2 TextureCoord;
		}

		private SampleDesc[] sampleDescs;
		private Vector3[][] accumTargets;

		/// <summary>
		/// Gets the width of this lightmap.
		/// </summary>
		public int Width { get { return width; } }
		/// <summary>
		/// Gets the height of this lightmap.
		/// </summary>
		public int Height { get { return height; } }
		/// <summary>
		/// Gets the number of subsamples per luxel side.
		/// </summary>
		/// <remarks>
		/// This is the number of samples per side, so the actual
		/// number of samples is <c>NumSamples</c> squared.
		/// </remarks>
		public int NumSamples { get { return samples; } }

		/// <summary>
		/// Computes the linear index of a sample.
		/// </summary>
		/// <param name="lx">The target luxel's x coordinate.</param>
		/// <param name="ly">The target luxel's y coordinate.</param>
		/// <param name="sx">The target sample's x coordinate.</param>
		/// <param name="sy">The target sample's y coordinate.</param>
		/// <returns>The linear index of the indicated sample.</returns>
		public int ComputeSampleIndex( int lx, int ly, int sx, int sy )
		{
			if( lx < 0 || lx > width || ly < 0 || ly > height ||
				sx < 0 || sx > samples || sy < 0 || sy > samples )
				throw new ArgumentOutOfRangeException();

			return ly * luxelPitch + sy * samplePitch + lx * samples + sx;
		}

		public IEnumerable<int> GetMappedSampleIndices()
		{
			for( int i = 0; i < sampleDescs.Length; i++ )
			{
				if( sampleDescs[i].Source != null )
					yield return i;
			}
		}

		public void GetSampleDesc( int lx, int ly, int sx, int sy, out SampleDesc desc )
		{
			int idx = ComputeSampleIndex( lx, ly, sx, sy );
			desc = sampleDescs[idx];
		}

		public void GetSampleDesc( int sampleIndex, out SampleDesc desc )
		{
			if( sampleIndex < 0 || sampleIndex > sampleDescs.Length )
				throw new ArgumentOutOfRangeException();

			desc = sampleDescs[sampleIndex];
		}

		public void GetLuxel( int accumTarget, int lx, int ly, int sx, int sy, out Vector3 luxel )
		{
			if( accumTarget < 0 || accumTarget > accumTargets.Length )
				throw new ArgumentOutOfRangeException();

			int idx = ComputeSampleIndex( lx, ly, sx, sy );
			luxel = accumTargets[accumTarget][idx];
		}

		public void GetLuxel( int accumTarget, int sampleIndex, out Vector3 luxel )
		{
			if( accumTarget < 0 || accumTarget > accumTargets.Length )
				throw new ArgumentOutOfRangeException();
			if( sampleIndex < 0 || sampleIndex > sampleDescs.Length )
				throw new ArgumentOutOfRangeException();

			luxel = accumTargets[accumTarget][sampleIndex];
		}

		public void SetLuxel( int accumTarget, int lx, int ly, int sx, int sy, ref Vector3 luxel )
		{
			if( accumTarget < 0 || accumTarget > accumTargets.Length )
				throw new ArgumentOutOfRangeException();

			int idx = ComputeSampleIndex( lx, ly, sx, sy );
			accumTargets[accumTarget][idx] = luxel;
		}

		public void SetLuxel( int accumTarget, int sampleIndex, ref Vector3 luxel )
		{
			if( accumTarget < 0 || accumTarget > accumTargets.Length )
				throw new ArgumentOutOfRangeException();
			if( sampleIndex < 0 || sampleIndex > sampleDescs.Length )
				throw new ArgumentOutOfRangeException();

			accumTargets[accumTarget][sampleIndex] = luxel;
		}

		public struct PointL
		{
			public long X, Y;

			public PointL( long x, long y )
			{
				X = x;
				Y = y;
			}

			public override string ToString()
			{
				return string.Format( "{0}, {1}", X, Y );
			}

			public static PointL operator +( PointL a, PointL b )
			{
				return new PointL( a.X + b.X, a.Y + b.Y );
			}

			public static PointL operator -( PointL a, PointL b )
			{
				return new PointL( a.X - b.X, a.Y - b.Y );
			}

			public static PointL operator -( PointL a )
			{
				return new PointL( -a.X, -a.Y );
			}
		}

		private const int SuperShift = 6;
		private const int SuperMult = 1 << SuperShift;
		private const int HalfSuper = SuperMult / 2;

		private const int BlockBits = 3;
		private const int BlockSize = 1 << BlockBits;

		private const int BleedFactor = 0;

		//triangle setup values
		private PointL p0, p1, p2, d01, d12, d20;
		private long c0, c1, c2;

		private IlluminationEngine.Surface source;
		private Interpolators interpolators;

		public void Map( IlluminationEngine.Surface source, ref Vertex v0, ref Vertex v1, ref Vertex v2 )
		{
			this.source = source;

			p0 = AdjustCoord( v0.LightmapCoord );
			p1 = AdjustCoord( v1.LightmapCoord );
			p2 = AdjustCoord( v2.LightmapCoord );

			//deltas
			d01 = p1 - p0;
			d12 = p2 - p1;
			d20 = p0 - p2;

			if( (d01.X == 0 && d01.Y == 0) ||
				(d12.X == 0 && d12.Y == 0) ||
				(d20.X == 0 && d20.Y == 0) )
				return;

			//bounding box (add and shift at the end is a ceil op)
			long xMin = (Helpers.Min( p0.X, p1.X, p2.X ) + (SuperMult - 1)) >> SuperShift;
			long yMin = (Helpers.Min( p0.Y, p1.Y, p2.Y ) + (SuperMult - 1)) >> SuperShift;
			long xMax = (Helpers.Max( p0.X, p1.X, p2.X ) + (SuperMult - 1)) >> SuperShift;
			long yMax = (Helpers.Max( p0.Y, p1.Y, p2.Y ) + (SuperMult - 1)) >> SuperShift;

			//adjust for bleed
			xMin -= 1;
			yMin -= 1;
			xMax += 1;
			yMax += 1;

			//clip!
			xMin = Helpers.Clamp( xMin, 0, width * samples );
			xMax = Helpers.Clamp( xMax, 0, width * samples );
			yMin = Helpers.Clamp( yMin, 0, height * samples );
			yMax = Helpers.Clamp( yMax, 0, height * samples );

			//half-edge constants (basically the line evaluated at the origin)
			c0 = d01.Y * p0.X - d01.X * p0.Y;
			c1 = d12.Y * p1.X - d12.X * p1.Y;
			c2 = d20.Y * p2.X - d20.X * p2.Y;

			//set up the interpolators
			interpolators = new Interpolators( ref v0, ref v1, ref v2, p0, p1, p2 );

			for( long y = yMin; y < yMax; y++ )
			{
				int yIdx = (int)y * samplePitch;

				for( long x = xMin; x < xMax; x++ )
				{
					HandleSample( x, y );
				}
			}

			this.source = null;
		}

		private PointL AdjustCoord( Vector2 v )
		{
			return new PointL(
				(long)((v.X * width) * samples * SuperMult),
				(long)((v.Y * height) * samples * SuperMult) );
		}

		private void HandleSample( long x, long y )
		{
			bool nxny, nxpy, pxny, pxpy;
			if( !TexelCheck( (x << SuperShift) + HalfSuper, (y << SuperShift) + HalfSuper,
				out nxny, out nxpy, out pxny, out pxpy ) )
				return;

			Shade( x, y );

			if( nxny )
			{
				if( x > 0 )
					Shade( x - 1, y );
					Shade( x - 1, y - 1 );
					Shade( x, y - 1 );
			}

			if( nxpy )
			{
				Shade( x - 1, y );
				Shade( x - 1, y + 1 );
				Shade( x, y + 1 );
			}

			if( pxny )
			{
				Shade( x + 1, y );
				Shade( x + 1, y - 1 );
				Shade( x, y - 1 );
			}

			if( pxpy )
			{
				Shade( x + 1, y );
				Shade( x + 1, y + 1 );
				Shade( x, y + 1 );
			}
		}

		private void Shade( long x, long y )
		{
			long GridHeight = height * samples;
			long GridWidth = width * samples;

			if( x < 0 || y < 0 )
				return;

			if( x >= GridWidth || y >= GridHeight )
				return;

			int iIdx = (int)(y * samplePitch + x);

			if( sampleDescs[iIdx].Source != null )
				return;

			sampleDescs[iIdx].Source = source;
			new InterpolatedValues( ref interpolators,
				(x << SuperShift) + HalfSuper,
				(y << SuperShift) + HalfSuper ).FillDesc( ref sampleDescs[iIdx] );
		}

		private bool BoxIntersectsTri( long bx0, long by0, long bx1, long by1 )
		{
			int triMask = 0;

			triMask |= (p0.X >= bx0) ? 0x0001 : 0;
			triMask |= (p1.X >= bx0) ? 0x0002 : 0;
			triMask |= (p2.X >= bx0) ? 0x0004 : 0;

			triMask |= (p0.X <= bx1) ? 0x0010 : 0;
			triMask |= (p1.X <= bx1) ? 0x0020 : 0;
			triMask |= (p2.X <= bx1) ? 0x0040 : 0;

			triMask |= (p0.Y >= by0) ? 0x0100 : 0;
			triMask |= (p1.Y >= by0) ? 0x0200 : 0;
			triMask |= (p2.Y >= by0) ? 0x0400 : 0;

			triMask |= (p0.Y <= by1) ? 0x1000 : 0;
			triMask |= (p1.Y <= by1) ? 0x2000 : 0;
			triMask |= (p2.Y <= by1) ? 0x4000 : 0;

			if( (triMask & 0x000F) == 0 || (triMask & 0x00F0) == 0 ||
				(triMask & 0x0F00) == 0 || (triMask & 0xF000) == 0 )
				//all three points outside of one box side
				return false;

			if( (triMask & 0x1111) == 0x1111 || (triMask & 0x2222) == 0x2222 ||
				(triMask & 0x4444) == 0x4444 )
				//a corner is inside the luxel
				return true;

			//return false;

			int boxMask = 0;

			boxMask |= (c0 + d01.X * by0 - d01.Y * bx0 >= 0) ? 0x001 : 0;
			boxMask |= (c0 + d01.X * by0 - d01.Y * bx1 >= 0) ? 0x002 : 0;
			boxMask |= (c0 + d01.X * by1 - d01.Y * bx0 >= 0) ? 0x004 : 0;
			boxMask |= (c0 + d01.X * by1 - d01.Y * bx1 >= 0) ? 0x008 : 0;

			boxMask |= (c1 + d12.X * by0 - d12.Y * bx0 >= 0) ? 0x010 : 0;
			boxMask |= (c1 + d12.X * by0 - d12.Y * bx1 >= 0) ? 0x020 : 0;
			boxMask |= (c1 + d12.X * by1 - d12.Y * bx0 >= 0) ? 0x040 : 0;
			boxMask |= (c1 + d12.X * by1 - d12.Y * bx1 >= 0) ? 0x080 : 0;

			boxMask |= (c2 + d20.X * by0 - d20.Y * bx0 >= 0) ? 0x100 : 0;
			boxMask |= (c2 + d20.X * by0 - d20.Y * bx1 >= 0) ? 0x200 : 0;
			boxMask |= (c2 + d20.X * by1 - d20.Y * bx0 >= 0) ? 0x400 : 0;
			boxMask |= (c2 + d20.X * by1 - d20.Y * bx1 >= 0) ? 0x800 : 0;

			if( (boxMask & 0x00F) == 0 || (boxMask & 0x0F0) == 0 || (boxMask & 0xF00) == 0 )
				//at least one edge excludes all four corners
				return false;

			//return false;

			if( (boxMask & 0x111) == 0x111 || (boxMask & 0x222) == 0x222 ||
				(boxMask & 0x444) == 0x444 || (boxMask & 0x888) == 0x888 )
				//at least one corner is inside the triangle
				return true;

			//return false;

			if( d01.X != 0 )
			{
				long yi;

				yi = (-c0 + bx0 * d01.Y) / d01.X;
				if( yi >= by0 && yi <= by1 )
					//contained intersection!
					return true;

				yi = (-c0 + bx1 * d01.Y) / d01.X;
				if( yi >= by0 && yi <= by1 )
					//contained intersection!
					return true;
			}

			if( d01.Y != 0 )
			{
				long xi;

				xi = (c0 + by0 * d01.X) / d01.Y;
				if( xi >= bx0 && xi <= bx1 )
					//contained intersection!
					return true;

				xi = (c0 + by1 * d01.X) / d01.Y;
				if( xi >= bx0 && xi <= bx1 )
					//contained intersection!
					return true;
			}

			if( d12.X != 0 )
			{
				long yi;

				yi = (-c1 + bx0 * d12.Y) / d12.X;
				if( yi >= by0 && yi <= by1 )
					//contained intersection!
					return true;

				yi = (-c1 + bx1 * d12.Y) / d12.X;
				if( yi >= by0 && yi <= by1 )
					//contained intersection!
					return true;
			}

			if( d12.Y != 0 )
			{
				long xi;

				xi = (c1 + by0 * d12.X) / d12.Y;
				if( xi >= bx0 && xi <= bx1 )
					//contained intersection!
					return true;

				xi = (c1 + by1 * d12.X) / d12.Y;
				if( xi >= bx0 && xi <= bx1 )
					//contained intersection!
					return true;
			}

			if( d20.X != 0 )
			{
				long yi;

				yi = (-c2 + bx0 * d20.Y) / d20.X;
				if( yi >= by0 && yi <= by1 )
					//contained intersection!
					return true;

				yi = (-c2 + bx1 * d20.Y) / d20.X;
				if( yi >= by0 && yi <= by1 )
					//contained intersection!
					return true;
			}

			if( d20.Y != 0 )
			{
				long xi;

				xi = (c2 + by0 * d20.X) / d20.Y;
				if( xi >= bx0 && xi <= bx1 )
					//contained intersection!
					return true;

				xi = (c2 + by1 * d20.X) / d20.Y;
				if( xi >= bx0 && xi <= bx1 )
					//contained intersection!
					return true;
			}

			return false;
		}

		private bool PointInTri( long px, long py )
		{
			//compute the four corners
			long bx0 = px - HalfSuper;
			long bx1 = px + HalfSuper;
			long by0 = py - HalfSuper;
			long by1 = py + HalfSuper;

			return BoxIntersectsTri( bx0, by0, bx1, by1 );
		}

		private bool TexelCheck( long cx, long cy,
			out bool linxNxNy, out bool linkNxPy,
			out bool linkPxNy, out bool linkPxPy )
		{
			long nx = cx - HalfSuper;
			long px = cx + HalfSuper;
			long ny = cy - HalfSuper;
			long py = cy + HalfSuper;

			if( !BoxIntersectsTri( nx, ny, px, py ) )
			{
				linxNxNy = false;
				linkNxPy = false;
				linkPxNy = false;
				linkPxPy = false;

				return false;
			}

			linxNxNy = BoxIntersectsTri( nx, ny, cx, cy );
			linkNxPy = BoxIntersectsTri( nx, cy, cx, py );
			linkPxPy = BoxIntersectsTri( cx, cy, px, py );
			linkPxNy = BoxIntersectsTri( cx, ny, px, cy );

			return true;
		}

		private struct Interpolator
		{
			public static double BeginSetup( ref PointL p0, ref PointL p1, ref PointL p2 )
			{
				return (long)(p1.X - p0.X) * (long)(p2.Y - p0.Y) - (long)(p2.X - p0.X) * (long)(p1.Y - p0.Y);
			}

#if true
			private double dx, dy, uOr;

			public static void Setup( PointL p0, PointL p1, PointL p2, double cVal, float u0, float u1, float u2, out Interpolator ret )
			{
				float a = (u2 - u0) * (p1.Y - p0.Y) - (u1 - u0) * (p2.Y - p0.Y);
				float b = (u1 - u0) * (p2.X - p0.X) - (u2 - u0) * (p1.X - p0.X);

				ret.dx = -a / cVal;
				ret.dy = -b / cVal;

				ret.uOr = u0 - p0.X * ret.dx - p0.Y * ret.dy;
			}

			public static float Compute( ref Interpolator interp, long x, long y )
			{
				return (float)(interp.uOr + x * interp.dx + y * interp.dy);
			}

			public static void StepValue( ref Interpolator interp, long dx, long dy, ref float value )
			{
				value = (float)(value + dx * interp.dx + dy * interp.dy);
			}
#else
			private float dx, dy, x0, y0, u0;

			public static void Setup( Point p0, Point p1, Point p2, float cVal, float u0, float u1, float u2, out Interpolator ret )
			{
				//u1 = 2 * u0 - u1;
				//u2 = 2 * u0 - u2;

				float a = (u2 - u0) * ((p1.Y - p0.Y) << SuperShift) - (u1 - u0) * ((p2.Y - p0.Y) << SuperShift);
				float b = (u1 - u0) * ((p2.X - p0.X) << SuperShift) - (u2 - u0) * ((p1.X - p0.X) << SuperShift);

				ret.dx = -a / cVal;
				ret.dy = -b / cVal;

				ret.x0 = p0.X;
				ret.y0 = p0.Y;
				ret.u0 = u0;
			}

			public static float Compute( ref Interpolator interp, int x, int y )
			{
				return interp.u0 +(x - interp.x0) * interp.dx + (y - interp.y0) * interp.dy;
			}

			public static void StepValue( ref Interpolator interp, int dx, int dy, ref float value )
			{
				value += dx * interp.dx + dy * interp.dy;
			}
#endif
		}

		private struct Interpolators
		{
			public Interpolator PositionX, PositionY, PositionZ;
			public Interpolator NormalX, NormalY, NormalZ;
			public Interpolator TangentX, TangentY, TangentZ;
			public Interpolator BinormalX, BinormalY, BinormalZ;

			public Interpolators( ref Vertex v0, ref Vertex v1, ref Vertex v2,
				PointL p0, PointL p1, PointL p2 )
			{
				double cVal = Interpolator.BeginSetup( ref p0, ref p1, ref p2 );

				Interpolator.Setup( p0, p1, p2, cVal, v0.Position.X, v1.Position.X, v2.Position.X, out PositionX );
				Interpolator.Setup( p0, p1, p2, cVal, v0.Position.Y, v1.Position.Y, v2.Position.Y, out PositionY );
				Interpolator.Setup( p0, p1, p2, cVal, v0.Position.Z, v1.Position.Z, v2.Position.Z, out PositionZ );

				Interpolator.Setup( p0, p1, p2, cVal, v0.Normal.X, v1.Normal.X, v2.Normal.X, out NormalX );
				Interpolator.Setup( p0, p1, p2, cVal, v0.Normal.Y, v1.Normal.Y, v2.Normal.Y, out NormalY );
				Interpolator.Setup( p0, p1, p2, cVal, v0.Normal.Z, v1.Normal.Z, v2.Normal.Z, out NormalZ );

				Interpolator.Setup( p0, p1, p2, cVal, v0.Tangent.X, v1.Tangent.X, v2.Tangent.X, out TangentX );
				Interpolator.Setup( p0, p1, p2, cVal, v0.Tangent.Y, v1.Tangent.Y, v2.Tangent.Y, out TangentY );
				Interpolator.Setup( p0, p1, p2, cVal, v0.Tangent.Z, v1.Tangent.Z, v2.Tangent.Z, out TangentZ );

				Interpolator.Setup( p0, p1, p2, cVal, v0.Binormal.X, v1.Binormal.X, v2.Binormal.X, out BinormalX );
				Interpolator.Setup( p0, p1, p2, cVal, v0.Binormal.Y, v1.Binormal.Y, v2.Binormal.Y, out BinormalY );
				Interpolator.Setup( p0, p1, p2, cVal, v0.Binormal.Z, v1.Binormal.Z, v2.Binormal.Z, out BinormalZ );
			}
		}

		private struct InterpolatedValues
		{
			public Vector3 Position;
			public Vector3 Normal;
			public Vector3 Tangent;
			public Vector3 Binormal;

			public InterpolatedValues( ref Interpolators interps, long x, long y )
			{
				Position.X = Interpolator.Compute( ref interps.PositionX, x, y );
				Position.Y = Interpolator.Compute( ref interps.PositionY, x, y );
				Position.Z = Interpolator.Compute( ref interps.PositionZ, x, y );

				Normal.X = Interpolator.Compute( ref interps.NormalX, x, y );
				Normal.Y = Interpolator.Compute( ref interps.NormalY, x, y );
				Normal.Z = Interpolator.Compute( ref interps.NormalZ, x, y );

				Tangent.X = Interpolator.Compute( ref interps.TangentX, x, y );
				Tangent.Y = Interpolator.Compute( ref interps.TangentY, x, y );
				Tangent.Z = Interpolator.Compute( ref interps.TangentZ, x, y );

				Binormal.X = Interpolator.Compute( ref interps.BinormalX, x, y );
				Binormal.Y = Interpolator.Compute( ref interps.BinormalY, x, y );
				Binormal.Z = Interpolator.Compute( ref interps.BinormalZ, x, y );
			}

			/*
			public void Step( ref Interpolators interps, long dx, long dy )
			{
				Interpolator.StepValue( ref interps.PositionX, dx, dy, ref Position.X );
				Interpolator.StepValue( ref interps.PositionY, dx, dy, ref Position.Y );
				Interpolator.StepValue( ref interps.PositionZ, dx, dy, ref Position.Z );

				Interpolator.StepValue( ref interps.NormalX, dx, dy, ref Normal.X );
				Interpolator.StepValue( ref interps.NormalY, dx, dy, ref Normal.Y );
				Interpolator.StepValue( ref interps.NormalZ, dx, dy, ref Normal.Z );
			}
			*/

			public void FillDesc( ref SampleDesc desc )
			{
				desc.Position = Position;
				desc.Normal = Normal;
				desc.Tangent = Tangent;
				desc.Binormal = Binormal;

				Helpers.SafeNormalize( ref desc.Normal );
				Helpers.SafeNormalize( ref desc.Tangent );
				Helpers.SafeNormalize( ref desc.Binormal );				
			}
		}

		public void BleedEdges( int iterations )
		{
			int[] map = ComputeBleedMap( iterations );

			Vector3[] dst = new Vector3[sampleCount];

			for( int accum = 0; accum < accumTargets.Length; accum++ )
			{
				Vector3[] src = accumTargets[accum];

				for( int s = 0; s < src.Length; s++ )
					dst[s] = src[map[s]];

				accumTargets[accum] = dst;
				dst = src;
			}
		}

		private int[] ComputeBleedMap( int iterations )
		{
			int[] src = new int[sampleCount];
			for( int i = 0; i < src.Length; i++ )
				src[i] = i;

			int[] dst = new int[sampleCount];

			int superWidth = width * samples;
			int superHeight = height * samples;

			for( int i = 0; i < iterations; i++ )
			{
				for( int y = 0; y < superHeight; y++ )
				{
					for( int x = 0; x < superWidth; x++ )
					{
						int iIdx = y * samplePitch + x;
						int mIdx = src[iIdx];

						dst[iIdx] = mIdx;

						if( sampleDescs[mIdx].Source != null )
							continue;

						foreach( Point b in BleedVecs )
						{
							int bx = x + b.X;
							int by = y + b.Y;

							if( bx < 0 || bx >= superWidth ||
								by < 0 || by >= superHeight )
								continue;

							int bIdx = by * samplePitch + bx;

							if( sampleDescs[src[bIdx]].Source != null )
							{
								dst[iIdx] = src[bIdx];
								break;
							}
						}
					}
				}

				Helpers.Swap( ref src, ref dst );
			}

			return src;
		}

		private static readonly Point[] BleedVecs = new Point[]
			{
				new Point( -1, -1 ),
				new Point( 0, -1 ),
				new Point( 1, -1 ),
				
				new Point( -1, 0 ),
				new Point( 1, 0 ),

				new Point( -1, 1 ),
				new Point( 0, 1 ),
				new Point( 1, 1 ),
			};

		public PixelBitmapContent<Vector3> Resolve( int accumTarget )
		{
			if( accumTarget < 0 || accumTarget > accumTargets.Length )
				throw new ArgumentOutOfRangeException();

			PixelBitmapContent<Vector3> ret = new PixelBitmapContent<Vector3>( width, height );

			Vector3[] target = accumTargets[accumTarget];

			for( int y = 0; y < height; y++ )
			{
				int yIdx = y * luxelPitch;

				for( int x = 0; x < width; x++ )
				{
					int count = 0;
					Vector3 value = new Vector3();

					int syIdx = yIdx + x * samples;
					for( int sy = 0; sy < samples; sy++ )
					{
						for( int sx = 0; sx < samples; sx++ )
						{
							int iIdx = syIdx + sx;

							//if( sampleDescs[iIdx].Source != null )
							{
								Vector3.Add( ref value, ref target[iIdx], out value );
								count++;
							}
						}

						syIdx += samplePitch;
					}

					if( count > 0 )
						Vector3.Multiply( ref value, 1.0F / count, out value );

					ret.SetPixel( x, y, value );
				}
			}

			return ret;
		}
	}
}
